package com.sias.judgeservice.judge;

import cn.hutool.json.JSONUtil;
import com.sias.common.common.ErrorCode;
import com.sias.common.exception.BusinessException;
import com.sias.judgeservice.judge.codsandbox.CodeSandbox;
import com.sias.judgeservice.judge.codsandbox.CodeSandboxFactory;
import com.sias.judgeservice.judge.codsandbox.CodeSandboxProxy;
import com.sias.judgeservice.judge.strategy.JudgeContext;
import com.sias.model.model.codsandbox.ExecuteCodeRequest;
import com.sias.model.model.codsandbox.ExecuteCodeResponse;
import com.sias.model.model.codsandbox.JudgeInfo;
import com.sias.model.model.dto.question.JudgeCase;
import com.sias.model.model.entity.Question;
import com.sias.model.model.entity.QuestionSubmit;
import com.sias.model.model.enums.QuestionSubmitStatusEnum;
import com.sias.serviceclient.service.QuestionFeignClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 吴文杰
 * @version 1.0
 * @since 2023/12/10 20:45
 */
@Service
public class JudgeServiceImpl implements JudgeService {

  @Resource
  private QuestionFeignClient questionFeignClient;

  @Resource
  private JudgeManager judgeManager;

  @Value("${sandbox.type}")
  private String type;

  @Override
  public QuestionSubmit doJudge(long questionSubmitId) {
    // 1）传入题目的提交 id，获取到对应的题目、提交信息（包含代码、编程语言等）
    QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
    if (questionSubmit == null) {
      throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在");
    }
    Long questionId = questionSubmit.getQuestionId();
    Question question = questionFeignClient.getQuestionById(questionId);
    if (question == null) {
      throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
    }
    // 2）如果题目提交状态不为等待中，就不用重复执行了
    if (!questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {
      throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");
    }
    // 3）更改判题（题目提交）的状态为 “判题中”，防止重复执行
    QuestionSubmit questionSubmitUpdate = new QuestionSubmit();
    questionSubmitUpdate.setId(questionSubmitId);
    questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
    boolean update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
    if (!update) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");
    }
    // 4）调用沙箱，获取到执行结果
    CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
    codeSandbox = new CodeSandboxProxy(codeSandbox);
    String language = questionSubmit.getLanguage();
    String code = questionSubmit.getCode();
    // 获取输入用例
    String judgeCaseStr = question.getJudgeCase();
    List<JudgeCase> judgeCaseList = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
    List<String> inputList = judgeCaseList.stream().map(JudgeCase::getInput).collect(Collectors.toList());
    ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
            .code(code)
            .language(language)
            .inputList(inputList)
            .build();
    ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
    List<String> outputList = executeCodeResponse.getOutputList();
    // 5）根据沙箱的执行结果，设置题目的判题状态和信息
    JudgeContext judgeContext = new JudgeContext();
    judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());
    judgeContext.setInputList(inputList);
    judgeContext.setOutputList(outputList);
    judgeContext.setJudgeCaseList(judgeCaseList);
    judgeContext.setQuestion(question);
    judgeContext.setQuestionSubmit(questionSubmit);
    JudgeInfo judgeInfo = judgeManager.doJudge(judgeContext);
    // 6）修改数据库中的判题结果
    questionSubmitUpdate = new QuestionSubmit();
    questionSubmitUpdate.setId(questionSubmitId);
    questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
    questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
    update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
    if (!update) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");
    }
    return questionFeignClient.getQuestionSubmitById(questionId);
  }
}
